#include "w25q128.h"

w25q128_data_t w25q128_data;

uint8_t rx_spi[4];
uint8_t W25QXX_BUFFER[4096];

void spi_send(uint8_t data_send)
{
	HAL_SPI_Transmit(&HSPI,&data_send,sizeof(data_send),0xFF);
}

uint8_t spi_send_read(uint8_t cmd)
{
	uint8_t rx_dat;
	HAL_SPI_TransmitReceive(&HSPI,&cmd,&rx_dat,1,0xFF);
	return rx_dat;
}

void w25q128_init()
{
	W25Q128_CS_L;
	spi_send(0xFF);
	w25q128_data.flash_type=w25q128_read_id();
	if(w25q128_data.flash_type!=0){
		w25q128_data.flash_init_flag=1;
	}
	W25Q128_CS_H;
}

void w25q128_send_address(uint32_t address)
{
	spi_send((uint8_t)(address>>16));
	spi_send((uint8_t)(address>>8));
	spi_send((uint8_t)(address));
}

uint16_t w25q128_read_id()
{
	uint8_t rx_data[2];
	uint16_t device_id;
	W25Q128_CS_L;
	spi_send(FLASH_ManufactDeviceID);
	w25q128_send_address(0x000000);
	rx_data[0] = spi_send_read(0xFF);
	rx_data[1] = spi_send_read(0xFF);
	W25Q128_CS_H;
	device_id = ((rx_data[0] << 8) | rx_data[1]);
	return device_id;
}

void w25q128_read_busy_if()
{
	uint8_t rec_data;
	W25Q128_CS_L;
	spi_send(FLASH_ReadStatusReg1);
	rec_data=spi_send_read(0xFF);
	W25Q128_CS_H;
	w25q128_data.flash_busy_flag=(rec_data & 0x01);
}

void w25q128_write_enable()
{
	W25Q128_CS_L;
	spi_send(FLASH_WriteEnable);
	W25Q128_CS_H;
}

void w25q128_wait_busy()
{
	w25q128_read_busy_if();
	while(w25q128_data.flash_busy_flag){w25q128_read_busy_if();osDelay(1);};
}

void w25q128_erase_chip()
{
	w25q128_write_enable();
	w25q128_wait_busy();
	W25Q128_CS_L;
	spi_send(FLASH_ChipErase);
	W25Q128_CS_H;
	w25q128_wait_busy();
}

void w25q128_erase_sector(uint32_t addr)
{
	w25q128_write_enable();
	w25q128_wait_busy();
	W25Q128_CS_L;
	spi_send(FLASH_SectorErase);
	w25q128_send_address(addr*4096);
	W25Q128_CS_H;
	w25q128_wait_busy();
}

uint8_t w25q128_read(uint8_t *puf,uint32_t addr,uint16_t datalen)
{
	uint8_t sum=0;
	W25Q128_CS_L;
	spi_send(FLASH_ReadData);
	w25q128_send_address(addr);
	for(uint16_t i=0;i<datalen;i++){
		puf[i]=spi_send_read(0xFF);
		sum+=puf[i];
	}
	W25Q128_CS_H;
	return sum;
}

void w25q128_write_page(uint8_t *puf,uint32_t addr,uint16_t datalen)
{
	w25q128_write_enable();
	W25Q128_CS_L;
	spi_send(FLASH_PageProgram);
	w25q128_send_address(addr);
	for(uint16_t i=0;i<datalen;i++){
		spi_send_read(puf[i]);
	}
	W25Q128_CS_H;
	w25q128_wait_busy();
}

void w25q128_write_page_nocheck(uint8_t *puf,uint32_t addr,uint16_t datalen)
{
	uint16_t pageremain;
	pageremain = 256 - addr % 256; 
	if (datalen <= pageremain)
		pageremain = datalen; 
	while (1)
	{
		w25q128_write_page(puf, addr, pageremain);
		if (datalen == pageremain)
			break; 
		else	   
		{
			puf += pageremain;
			addr += pageremain;
 
			datalen -= pageremain; 
			if (datalen > 256)
				pageremain = 256; 
			else
				pageremain = datalen; 
		}
	};

}

void w25q128_write(uint8_t *puf,uint32_t addr,uint16_t datalen)
{
	uint32_t secpos;					
	uint16_t secoff;					
	uint16_t secremain;				
	uint16_t i;						
	uint8_t *W25QXX_BUF;				
	W25QXX_BUF = W25QXX_BUFFER; 
	secpos = addr / 4096;	
	secoff = addr % 4096;	
	secremain = 4096 - secoff;	
	if (datalen <= secremain)
		secremain = datalen; 
	while (1)
	{
		w25q128_read(W25QXX_BUF, secpos * 4096, 4096); 
		for (i = 0; i < secremain; i++)				  
		{
			if (W25QXX_BUF[secoff + i] != 0XFF)
				break; 
		}
		if (i < secremain) 
		{
			w25q128_erase_sector(secpos);	
			for (i = 0; i < secremain; i++) 
			{
				W25QXX_BUF[i + secoff] = puf[i];
			}
			w25q128_write_page_nocheck(W25QXX_BUF, secpos * 4096, 4096); 
		}
		else
			w25q128_write_page_nocheck(puf, addr, secremain); 
		if (datalen == secremain)
			break; 
		else	   
		{
			secpos++;	
			secoff = 0; 
 
			puf += secremain;		 
			addr += secremain;		 
			datalen -= secremain; 
			if (datalen > 4096)
				secremain = 4096; 
			else
				secremain = datalen; 
		}
	};

}

void w25q128_config_init()
{
	w25q128_data.flash_init_flag=0;
	w25q128_data.flash_busy_flag=0;
	w25q128_data.flash_erase_all_flag=0;
	w25q128_data.flash_erase_addr_set=0;
	w25q128_data.flash_erase_addr_set=0;
	w25q128_data.flash_write_flag=0;
	w25q128_data.flash_write_len=0;
	w25q128_data.flash_read_flag=0;
	w25q128_data.flash_read_len=0;
	buf_clear(w25q128_data.flash_write_buf,256);
	// for(int i=0;i<256;i++){
	// 	w25q128_data.flash_write_buf[i]=0xFF;
	// }
}

void buf_clear(uint8_t *buf,uint16_t len)
{
	for(int i=0;i<len;i++){
		buf[i]=0xFF;
	}
}

